package com.songlanyun.modules.rabbitmq.config;

import com.songlanyun.modules.rabbitmq.DelayedConstant;
import com.songlanyun.modules.rabbitmq.MqConstant;
import com.songlanyun.modules.rabbitmq.service.MsgLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import static com.songlanyun.modules.rabbitmq.MqConstant.*;
import static com.songlanyun.modules.rabbitmq.MqConstant.WxNotifyConstant.WX_NOTIFY_EXCHANGE;

@Configuration
@Slf4j
/**
 * rabitmq 配置及 生产者到中间件出错处理
 */
public class RabbitConfig {

    // @Resource
    //PropertiesConfig propertiesConfig;

    @Autowired
    private CachingConnectionFactory connectionFactory;

    @Autowired
    private MsgLogService msgLogService;

    private RabbitTemplate rabbitTemplate;

    @Bean
    public RabbitAdmin rabbitAdmin() {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(converter());

        // 消息是否成功发送到Exchange
       /*
       "发送成功"是消息发送到exchange这个里面发送成功了，这个发送成功的回到函数是，confirmCallback，
       而是否能够发送到队列成功的回调函数是，returnCallback，注意区分开

       实际工作中，我们可以继续进行后续的处理，比如消息发送失败了该如何处理，如何第一时间反馈到开发人员进行问题的排查，
       都可以在回调函数里面做一些处理的，*/
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("消息成功发送到Exchange");
                String msgId = correlationData.getId();
                msgLogService.updateStatus(msgId, MqConstant.MsgLogStatus.DELIVER_SUCCESS);
            } else {
                log.info("消息发送到Exchange失败, {}, cause: {}", correlationData, cause);
            }
        });

        // 触发setReturnCallback回调必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调
        rabbitTemplate.setMandatory(true);
        // 消息是否从Exchange路由到Queue, 注意: 这是一个失败回调, 只有消息从Exchange路由到Queue失败才会回调这个方法
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            if(exchange.indexOf("_delayed")==-1)
            {
                log.info("消息从Exchange路由到Queue失败: exchange: {}, route: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message);
            }
        });

        return rabbitTemplate;
    }

    @Bean
    public Jackson2JsonMessageConverter converter() {
        return new Jackson2JsonMessageConverter();
    }


    /**
     * 针对消费者配置
     * 1. 设置交换机类型
     * 2. 将队列绑定到交换机
     FanoutExchange: 将消息分发到所有的绑定队列，无routingkey的概念
     HeadersExchange ：通过添加属性key-value匹配
     DirectExchange:按照routingkey分发到指定队列
     TopicExchange:多关键字匹配
     */

    //region  分润交换机  分润队列 分润队列与交换机绑定

    /**
     * 分润交换机
     *
     * @return
     */
    @Bean
    public DirectExchange proceedsExchange() {
        return new DirectExchange(PROCEEDS_EXCHANGE);
    }

    /**
     * 分润队列
     *
     * @return
     */
    @Bean
    public Queue proceedsQueue() {
        return new Queue(PROCEEDS_QUEUE, true); //队列持久
    }

    /**
     * 分润队列与交换机绑定
     *
     * @return
     */
    @Bean
    public Binding proceedsBinding() {
        return BindingBuilder.bind(proceedsQueue()).to(proceedsExchange()).with(PROCEEDS_ROUTER);
    }

    //endregion

    //region  会员升级交换机  会员升级队列 会员升级队列与会员升级机绑定

    /**
     * 会员升级交换机
     *
     * @return
     */
    @Bean
    public DirectExchange userUpgradeExchange() {
        return new DirectExchange(UserUpgradeConstant.USER_UPGRADE_EXCHANGE);
    }

    /**
     * 会员升级队列
     *
     * @return
     */
    @Bean
    public Queue userUpgradeQueue() {
        return new Queue(UserUpgradeConstant.USER_UPGRADE_QUEUE, true);
    }

    /**
     * 会员升级队列与会员升级机绑定
     *
     * @return
     */
    @Bean
    public Binding userUpgradeBinding() {
        return BindingBuilder.bind(userUpgradeQueue()).to(userUpgradeExchange()).with(UserUpgradeConstant.USER_UPGRADE_ROUTER);
    }

    //endregion

    //region  计算商家所润与分润交换机  计算商家所润与分润升级队列  计算商家所润与分润队列和交换机绑定

    /**
     * 计算商家所润与分润交换机
     *
     * @return
     */
    @Bean
    public DirectExchange computeShopGainsExchange() {
        return new DirectExchange(ComputeShopGainsConstant.COMPUTE_SHOP_GAINS_EXCHANGE);
    }

    /**
     * 计算商家所润与分润升级队列
     *
     * @return
     */
    @Bean
    public Queue computeShopGainsQueue() {
        return new Queue(ComputeShopGainsConstant.COMPUTE_SHOP_GAINS_QUEUE, true);
    }

    /**
     * 计算商家所润与分润队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding computeShopGainsBinding() {
        return BindingBuilder.bind(computeShopGainsQueue()).to(computeShopGainsExchange()).with(ComputeShopGainsConstant.COMPUTE_SHOP_GAINS_ROUTER);
    }

    //endregion

    //region  发送微信小程序消息

    /**
     * 发送微信小程序消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange wxNotifyExchange() {
        return new DirectExchange(WxNotifyConstant.WX_NOTIFY_EXCHANGE);
    }

    /**
     * 发送微信小程序消息队列
     *
     * @return
     */
    @Bean
    public Queue wxNotifyQueue() {
        return new Queue(WxNotifyConstant.WX_NOTIFY_QUEUE, true);
    }

    /**
     * 发送微信小程序消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding wxNotifyRouter() {
        return BindingBuilder.bind(wxNotifyQueue()).to(wxNotifyExchange()).with(WxNotifyConstant.WX_NOTIFY_ROUTER);
    }

    //endregion

    //region  发货成功发送消息

    /**
     * 发货成功发送消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange shipSuccessSendMessageExchange() {
        return new DirectExchange(ShipSuccessSendMessageConstant.SHIP_SUCCESS_SEND_MESSAGE_EXCHANGE);
    }

    /**
     * 发送微信小程序消息队列
     *
     * @return
     */
    @Bean
    public Queue shipSuccessSendMessageQueue() {
        return new Queue(ShipSuccessSendMessageConstant.SHIP_SUCCESS_SEND_MESSAGE_QUEUE, true);
    }

    /**
     * 发货成功发送消息 消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding shipSuccessSendMessageERouter() {
        return BindingBuilder.bind(shipSuccessSendMessageQueue()).to(shipSuccessSendMessageExchange()).with(ShipSuccessSendMessageConstant.SHIP_SUCCESS_SEND_MESSAGE_ROUTER);
    }

    //endregion

    //region  商家处理维权发送消息

    /**
     * 商家处理维权发送消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange businessRightsSendMessageExchange() {
        return new DirectExchange(BusinessRightsSendMessageConstant.BUSINESS_RIGHTS_SEND_MESSAGE_EXCHANGE);
    }

    /**
     * 商家处理维权发送消息消息队列
     *
     * @return
     */
    @Bean
    public Queue businessRightsSendMessageQueue() {
        return new Queue(BusinessRightsSendMessageConstant.BUSINESS_RIGHTS_SEND_MESSAGE_QUEUE, true);
    }

    /**
     * 商家处理维权发送消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding businessRightsSendMessageRouter() {
        return BindingBuilder.bind(businessRightsSendMessageQueue()).to(businessRightsSendMessageExchange()).with(BusinessRightsSendMessageConstant.BUSINESS_RIGHTS_SEND_MESSAGE_ROUTER);
    }

    //endregion

    //region  保存积分记录发送消息

    /**
     * 保存积分记录发送消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange integralRecordSendMessageExchange() {
        return new DirectExchange(IntegralRecordSendMessageConstant.INTEGRAL_RECORD_SEND_MESSAGE_EXCHANGE);
    }

    /**
     * 保存积分记录发送消息消息队列
     *
     * @return
     */
    @Bean
    public Queue integralRecordSendMessageQueue() {
        return new Queue(IntegralRecordSendMessageConstant.INTEGRAL_RECORD_SEND_MESSAGE_QUEUE, true);
    }

    /**
     * 保存积分记录发送消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding integralRecordSendMessageRouter() {
        return BindingBuilder.bind(integralRecordSendMessageQueue()).to(integralRecordSendMessageExchange()).with(IntegralRecordSendMessageConstant.INTEGRAL_RECORD_SEND_MESSAGE_ROUTER);
    }

    //endregion

    //region  APP站内消息

    /**
     * APP站内消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange privateMessageSendMessageExchange() {
        return new DirectExchange(PrivateMessageSendMessageConstant.PRIVATE_MESSAGE_SEND_MESSAGE_EXCHANGE);
    }

    /**
     * APP站内消息消息队列
     *
     * @return
     */
    @Bean
    public Queue privateMessageSendMessageQueue() {
        return new Queue(PrivateMessageSendMessageConstant.PRIVATE_MESSAGE_SEND_MESSAGE_QUEUE, true);
    }

    /**
     * APP站内消息消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding privateMessageSendMessageRouter() {
        return BindingBuilder.bind(privateMessageSendMessageQueue()).to(privateMessageSendMessageExchange()).with(PrivateMessageSendMessageConstant.PRIVATE_MESSAGE_SEND_MESSAGE_ROUTER);
    }

    //endregion

    //region  库存不足记录

    /**
     * 库存不足记录交换机
     *
     * @return
     */
    @Bean
    public DirectExchange underStockRecordSendMessageExchange() {
        return new DirectExchange(UnderStockRecordConstant.UNDER_STOCK_RECORD_EXCHANGE);
    }

    /**
     * 库存不足记录消息队列
     *
     * @return
     */
    @Bean
    public Queue underStockRecordSendMessageQueue() {
        return new Queue(UnderStockRecordConstant.UNDER_STOCK_RECORD_QUEUE, true);
    }

    /**
     * 库存不足记录消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding underStockRecordSendMessageRouter() {
        return BindingBuilder.bind(underStockRecordSendMessageQueue()).to(underStockRecordSendMessageExchange()).with(UnderStockRecordConstant.UNDER_STOCK_RECORD_ROUTING);
    }

    //endregion

    //region  更新库存不足的单品记录状态

    /**
     * 更新库存不足的单品记录状态交换机
     *
     * @return
     */
    @Bean
    public DirectExchange underStockStatusSendMessageExchange() {
        return new DirectExchange(UnderStockStatusConstant.UNDER_STOCK_STATUS_EXCHANGE);
    }

    /**
     * 更新库存不足的单品记录状态消息队列
     *
     * @return
     */
    @Bean
    public Queue underStockStatusSendMessageQueue() {
        return new Queue(UnderStockStatusConstant.UNDER_STOCK_STATUS_QUEUE, true);
    }

    /**
     * 更新库存不足的单品记录状态消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding underStockStatusSendMessageRouter() {
        return BindingBuilder.bind(underStockStatusSendMessageQueue()).to(underStockStatusSendMessageExchange()).with(UnderStockStatusConstant.UNDER_STOCK_STATUS_ROUTING);
    }

    //endregion

    //region  公益消息

    /**
     * 公益消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange publicBenefitMsgSendMessageExchange() {
        return new DirectExchange(PublicBenefitMsgConstant.PUBLIC_BENEFIT_MSG_EXCHANGE);
    }

    /**
     * 公益消息消息队列
     *
     * @return
     */
    @Bean
    public Queue publicBenefitMsgSendMessageQueue() {
        return new Queue(PublicBenefitMsgConstant.PUBLIC_BENEFIT_MSG_QUEUE, true);
    }

    /**
     * 公益消息消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding publicBenefitMsgSendMessageRouter() {
        return BindingBuilder.bind(publicBenefitMsgSendMessageQueue()).to(publicBenefitMsgSendMessageExchange()).with(PublicBenefitMsgConstant.PUBLIC_BENEFIT_MSG_ROUTING);
    }

    //endregion


    //region  会员注册成功发送消息

    /**
     * 会员注册成功消息交换机
     *
     * @return
     */
    @Bean
    public DirectExchange userRegSuccessSendMessageExchange() {
        return new DirectExchange(UserRegSuccessConstant.USER_REG_SUCCESS_EXCHANGE);
    }

    /**
     * 会员注册成功消息队列
     *
     * @return
     */
    @Bean
    public Queue userRegSuccessSendMessageQueue() {
        return new Queue(UserRegSuccessConstant.USER_REG_SUCCESS_QUEUE, true);
    }

    /**
     * 会员注册成功消息队列和交换机绑定
     *
     * @return
     */
    @Bean
    public Binding userRegSuccessSendMessageRouter() {
        return BindingBuilder.bind(userRegSuccessSendMessageQueue()).to(userRegSuccessSendMessageExchange()).with(UserRegSuccessConstant.USER_REG_SUCCESS_ROUTER);
    }

    //endregion
}
